package econosim;

import java.util.*;
import simframe.simulation.*;

/*
 * The Market class provides the interaction between
 * consumers and producers
 */

public abstract class Market extends SimObject {

    // actions supported by a market
     public final Action processOrders = new Action() {

        public void exec() {
            processOrders();
        }
    };

      public final Action clearMarket = new Action() {

        public void exec() {
            clearMarket();
        }
    };



    // execute a purchase order
    // if anything is left over afterwards a new purchase order is created
    protected Offer executePurchaseOrder(Offer sell, Offer purchaseOrder) {

        double brokerFee = fee(purchaseOrder.getAgent(), sell.getAgent());
        double cost = sell.getCost();
        double util = sell.getUtility();

        double deltaU = purchaseOrder.getUtility() - util;
        double deltaMoney = purchaseOrder.getPrice() - cost;
        deltaMoney -= brokerFee;


        sell.getAgent().receiveMoney(cost);
        sell.getAgent().reduceUtility(util);

        purchaseOrder.getAgent().payMoney(cost + brokerFee);
        purchaseOrder.getAgent().addUtility(util);

        if (deltaU > 0 && deltaMoney > 0) {
            return new Offer(deltaU, deltaMoney, purchaseOrder.getType(), purchaseOrder.getAgent(), purchaseOrder.getDuration());
        }

        return null;
    }

	protected Map<Offer.Type, Map<TransActionAgent, Queue<Offer>>> offers = new HashMap<Offer.Type, Map<TransActionAgent, Queue<Offer>>>();

	public synchronized void addOffer(Offer offer) {

        TransActionAgent agent = offer.getAgent();

        Map<TransActionAgent, Queue<Offer>> agentOfferList = offers.get(offer.getType());

        if (agentOfferList == null) {
            agentOfferList = new HashMap<TransActionAgent, Queue<Offer>>();

            offers.put(offer.getType(), agentOfferList);
        }

        Queue<Offer> offerList = agentOfferList.get(agent);

        if (offerList == null) {
            offerList = new LinkedList<Offer>();
            agentOfferList.put(agent, offerList);
        }

        offerList.add(offer);
	}
	
	   
    /*
     * clear all offers that are defunk
     */
    public synchronized void clearMarket() {
        LinkedList<Offer> rerun = new LinkedList<Offer>();
        
        for (Map<TransActionAgent, Queue<Offer>> agentOfferList : offers.values()) {
            for (Queue<Offer> offerList : agentOfferList.values()) {

                rerun.clear();

                while (!offerList.isEmpty()) {
                    Offer offer = offerList.remove();


                    offer.decrementTime();

                    if (offer.good()) {
                       rerun.push(offer);
                    }
                }
                
                offerList.addAll(rerun);
            }
        }
    }

    protected abstract void processOrders();
	

	public boolean dead() {
		// market never dies
		return false;
	}

	public void reset() {
		clearAllOffers();
	}
	
	   // clear all offers added by agent
    public void clearOffers(TransActionAgent agent, Offer.Type type) {

        Map<TransActionAgent, Queue<Offer>> agentOfferList = offers.get(type);

        if (agentOfferList != null) {
            Queue<Offer> offerList = agentOfferList.get(agent);

            if (offerList != null) {
                offerList.clear();
            }
        }
    }

    // clears the lists
	public void clearAllOffers() {
		  for(Map<TransActionAgent, Queue<Offer>> agentOfferList : offers.values()) {
              for( Queue<Offer> offerList : agentOfferList.values()) {
                  offerList.clear();
              }
          }
	}
	
	public double  fee(TransActionAgent agent1, TransActionAgent agent2) {
		return 0.0;
	}
}
